﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Shapes;
using ZedGraph;

namespace vJine.Lua.GUI.UI {
    /// <summary>
    /// ZedGraphViewer.xaml 的交互逻辑
    /// </summary>
    public partial class ZedGraphViewer : UserControl {
        public ZedGraphViewer() {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e) {
            
        }

        public void init_ber(string title, double x_min, double x_max) {
            LogLogScaleHelper.Init(this.GraphViewer, title, x_min, x_max);
        }

        List<PointPairList> PPL;
        public void init(string graph_title, string x_title, double x_max, YaxisConfig[] yAxis) {
            this.PPL = LivGraphHelper.Init(this.GraphViewer, graph_title, x_title, x_max, yAxis);
        }

        public void update(double x, double[] Ys) {
            for (int i = 0; i < this.PPL.Count; i++) {
                PointPairList ppl_i = this.PPL[i];
                ppl_i.Add(x, Ys[i]);
            }
            
            this.GraphViewer.GraphPane.AxisChange();
            this.GraphViewer.Refresh();
        }

        public void save(string file_name) {
            this.GraphViewer.SaveAs(file_name + ".bmp");
        }

        public class YaxisConfig {
            public string title { get; set; }
            public string color { get; set; }
            public double? min { get; set; }
            public double? max { get; set; }
        }

        class LivGraphHelper {
            public static List<PointPairList> Init(ZedGraphControl GraphViewer, string graph_title, string x_title, double x_max, YaxisConfig[] yAxis) {

                GraphViewer.GraphPane.Legend.IsVisible = false;
                GraphViewer.GraphPane.IsFontsScaled = false;

                GraphViewer.GraphPane.Title.FontSpec.IsBold = false;
                GraphViewer.GraphPane.Title.Text = graph_title;
                GraphViewer.GraphPane.XAxis.Title.Text = x_title;
                GraphViewer.GraphPane.XAxis.Scale.MaxAuto = false;
                GraphViewer.GraphPane.XAxis.Scale.Max = x_max;
                GraphViewer.GraphPane.YAxis.MajorGrid.IsVisible = true;
                GraphViewer.GraphPane.YAxis.MajorGrid.Color = Color.Gray;
                GraphViewer.GraphPane.YAxis.MajorGrid.DashOff = 0;
                GraphViewer.GraphPane.YAxisList.Clear();

                List<PointPairList> PPL = new List<PointPairList>();
                //PointPairList ppl = new PointPairList();
                //PPL.Add(ppl);
                //*************Init Curve******************//
                //SetAxis(GraphViewer, 0, "Laser CW Power Out,Pop(mW)", Color.Blue, ppl, fontSize, 1.7F);
                //PPL.Add(SetAxis(GraphViewer, "Laser CW Power Out,Pop(mW)", Color.Blue, 1.7F));
                //PPL.Add(SetAxis(GraphViewer, "Monitor Current,Ipd(uA)", Color.Black, 1.7F));
                //SetAxis(GraphViewer, "Forward Voltage,VF(V)", Color.Red, 1.7F);
                //PPL.Add(SetAxis(GraphViewer, "dl/di(mW/mA)", Color.Pink, 1.3F));
                for (int i = 0; i < yAxis.Length; i++) {
                    YaxisConfig yConfig_i = yAxis[i];
                    Color axisColor = Color.Black;
                    if (!string.IsNullOrEmpty(yConfig_i.color)) {
                        axisColor = Color.FromName(yConfig_i.color);
                    }
                    PPL.Add(SetAxis(GraphViewer, yConfig_i.title, axisColor, 1.7F));
                }

                GraphViewer.GraphPane.AxisChange();
                GraphViewer.Refresh();

                return PPL;
            }

            #region Graph
            static PointPairList SetAxis(ZedGraphControl GraphViewer, string title, Color color, float lineWidth) {
                float fontSize = 12;
                PointPairList ppl = new PointPairList();
                int axisIndex  = GraphViewer.GraphPane.AddYAxis(title);
                SetAxis(GraphViewer, axisIndex, title, color, ppl, fontSize, lineWidth);
                GraphViewer.GraphPane.YAxisList[axisIndex].Scale.MinAuto = false;
                GraphViewer.GraphPane.YAxisList[axisIndex].Scale.Min = 0;
                GraphViewer.GraphPane.YAxisList[axisIndex].Scale.MaxAuto = true;
                return ppl;
            }
            static LineItem SetAxis(ZedGraphControl GraphViewer, int axisIndex, string title, System.Drawing.Color color, PointPairList ppl, float fontSize, float lineWidth) {
                LineItem li = SetAxis(GraphViewer, GraphViewer.GraphPane.YAxisList[axisIndex], title, color, ppl, fontSize, lineWidth);
                li.YAxisIndex = axisIndex;

                return li;
            }

           static LineItem SetAxis(ZedGraphControl GraphViewer, Axis axis, string title, System.Drawing.Color color, PointPairList ppl, float fontSize, float lineWidth) {
                axis.Color = color; 
                axis.Scale.FontSpec.FontColor = color;
                axis.Scale.FontSpec.Size = fontSize;
                axis.Scale.Min = 0;
                axis.Scale.MinAuto = false;

                AxisLabel yAxisTitle = axis.Title;
                yAxisTitle.FontSpec.Size = fontSize;
                yAxisTitle.Text = title;
                yAxisTitle.Gap = 0;
                yAxisTitle.FontSpec.FontColor = color;
                yAxisTitle.FontSpec.IsBold = false;

                LineItem li = GraphViewer.GraphPane.AddCurve(title, ppl, color, SymbolType.None);
                li.Line.Width = lineWidth;
                li.Line.IsAntiAlias = true;
                return li;
            }
            #endregion
        }
        

        Dictionary<string, LineObj> Lines = new Dictionary<string, LineObj>();
        Dictionary<string, List<double>> XY = new Dictionary<string,List<double>>();
        public void new_point(string berTitle, double x, double y) {
            if (!this.Lines.ContainsKey(berTitle)) {
                this.Lines.Add(berTitle, new LineObj());
            }
            if (!this.XY.ContainsKey(berTitle)) {
                this.XY.Add(berTitle, new List<double>());
            }
            LineObj line = this.Lines[berTitle];
            List<double> xy = this.XY[berTitle];

            xy.Add(x); xy.Add(LogLogScaleHelper.ConvertLogLogScale(y));
            LogLogScaleHelper.genPoint(this.GraphViewer, x, y, Color.Yellow);

            if (xy.Count > 2) {
                this.Lines.Remove(berTitle);
                this.Lines.Add(berTitle,LogLogScaleHelper.fit_line(this.GraphViewer, line, xy));
            }
            
            this.UpdateLayout();
        }

        public class LogLogScaleHelper {
            public static double[] LogStandard =
                new double[] {
                Math.Log10(2), Math.Log10(3), Math.Log10(4),
                Math.Log10(5), Math.Log10(6), Math.Log10(7),
                Math.Log10(8), Math.Log10(9), Math.Log10(10) 
            };
            public static double[] LogY = new double[13];
            static LogLogScaleHelper() {
                for (int i = 0; i <= 12; i++) {
                    LogY[i] = Math.Log10(i + 4);
                }
            }

            public static readonly double sensivity_ber = -Math.Log10(-Math.Log10(1E-12));

            public static double ConvertLogLogScale(double ber) {
                if (ber > 1E-4) {
                    return -LogY[0] + (Math.Log10(1E+04 * ber) * (LogY[0] - LogStandard[1]));
                } else if (ber >= 1E-5 && ber < 1E-4) {
                    return -LogY[1] + (Math.Log10(1E+05 * ber) * (LogY[1] - LogY[0]));
                } else if (ber >= 1E-6 && ber < 1E-5) {//1e-6 <= case < 1e-5
                    return -LogY[2] + (Math.Log10(1E+06 * ber) * (LogY[2] - LogY[1]));
                } else if (ber >= 1E-7 && ber < 1E-6) {//1e-7 <= case < 1e-6
                    return -LogY[3] + (Math.Log10(1E+07 * ber) * (LogY[3] - LogY[2]));
                } else if (ber >= 1E-08 && ber < 1E-7) {//1e-8 <= case < 1e-7
                    return -LogY[4] + (Math.Log10(1E+08 * ber) * (LogY[4] - LogY[3]));
                } else if (ber >= 1E-09 && ber < 1E-08) {    //1e-9 <= case < 1e-8
                    return -LogY[5] + (Math.Log10(1E+09 * ber) * (LogY[5] - LogY[4]));
                } else if (ber >= 1E-10 && ber < 1E-09) {//1e-10 <= case < 1e-9
                    return -LogY[6] + (Math.Log10(1E+10 * ber) * (LogY[6] - LogY[5]));
                } else if (ber >= 1E-11 && ber < 1E-10) {//1e-11 <= case < 1e-10
                    return -LogY[7] + (Math.Log10(1E+11 * ber) * (LogY[7] - LogY[6]));
                } else if (ber >= 1E-12 && ber < 1E-11) {//1e-12 <= case < 1e-11
                    return -LogY[8] + (Math.Log10(1E+12 * ber) * (LogY[8] - LogY[7]));
                } else if (ber >= 1E-13 && ber < 1E-12) {//1e-13 <= case < 1e-12
                    return -LogY[9] + (Math.Log10(1E+13 * ber) * (LogY[9] - LogY[8]));
                } else if (ber >= 1E-14 && ber < 1E-13) {//1e-14 <= case < 1e-13
                    return -LogY[10] + (Math.Log10(1E+14 * ber) * (LogY[10] - LogY[9]));
                } else if (ber >= 1E-15 && ber < 1E-14) {//1e-15 <= case < 1e-14
                    return -LogY[11] + (Math.Log10(1E+15 * ber) * (LogY[11] - LogY[10]));
                } else if (ber >= 1E-16 && ber < 1E-15) {
                    return -LogY[12] + (Math.Log10(1E+16 * ber) * (LogY[12] - LogY[11]));
                } else {
                    return 0;
                }
            }

            public static void Init(ZedGraph.ZedGraphControl GraphViewer, string title, double x_min, double x_max) {
                GraphViewer.GraphPane.GraphObjList.Clear();

                GraphViewer.GraphPane.Title.Text = title;
                GraphViewer.GraphPane.XAxis.Title.Text = "Power(dbm)";
                GraphViewer.GraphPane.YAxis.Title.Text = "BER";

                GraphViewer.GraphPane.YAxis.MajorGrid.IsVisible = false;
                GraphViewer.GraphPane.YAxis.MinorGrid.IsVisible = false;
                GraphViewer.GraphPane.YAxis.MajorTic.IsAllTics = false;
                GraphViewer.GraphPane.YAxis.MinorTic.IsAllTics = false;

                GraphViewer.GraphPane.YAxis.Scale.MinAuto = false;
                GraphViewer.GraphPane.YAxis.Scale.Min = -LogY[8];
                GraphViewer.GraphPane.YAxis.Scale.MaxAuto = false;
                GraphViewer.GraphPane.YAxis.Scale.Max = -LogY[0];

                GraphViewer.GraphPane.YAxis.Scale.FontSpec.FontColor = Color.White;
                GraphViewer.GraphPane.YAxis.Scale.LabelGap = 1.3F;

                GraphViewer.GraphPane.XAxis.Scale.MinAuto = false;
                GraphViewer.GraphPane.XAxis.Scale.Min = x_min;
                GraphViewer.GraphPane.XAxis.Scale.MaxAuto = false;
                GraphViewer.GraphPane.XAxis.Scale.Max = x_max;

                int Increment = 0; double y = 0;
                for (int i = 0; i <= 72; i++) {
                    if (i % 9 == 0) {
                        y = -LogY[i / 9];
                        Increment += 1;
                    } else {
                        y =
                            -LogY[Increment] + ((LogStandard[i % 8]) * ((LogY[Increment] - LogY[Increment - 1])));
                    }

                    LineObj line =
                            new LineObj(GraphViewer.GraphPane.XAxis.Scale.Min, y, GraphViewer.GraphPane.XAxis.Scale.Max, y);
                    line.Location.CoordinateFrame = CoordType.AxisXYScale;
                    line.Line.IsAntiAlias = true;
                    line.Line.Width = i % 9 == 0 ? 1 : 0.1F;
                    line.Line.Color = i % 9 == 0 ? Color.Red : Color.Gray;
                    GraphViewer.GraphPane.GraphObjList.Add(line);

                    if (i % 9 == 0) {
                        TextObj text = new TextObj("10^-" + (i / 9 + 4).ToString("00"), x_min - 0.5, y);
                        text.Location.CoordinateFrame = CoordType.AxisXYScale;
                        text.FontSpec.Border.IsVisible = false;
                        GraphViewer.GraphPane.GraphObjList.Add(text);
                    }
                }

                //test_points(zgc);
                GraphViewer.GraphPane.AxisChange();
                GraphViewer.Refresh();
            }

            public static void genPoint(ZedGraphControl zgc, double x, double y, Color color) {
                ZedGraph.EllipseObj point = new EllipseObj(x, ConvertLogLogScale(y), 0.07, 0.007);
                point.Location.CoordinateFrame = CoordType.AxisXYScale;
                point.Border.IsAntiAlias = true;
                point.Fill.Color = color;
                zgc.GraphPane.GraphObjList.Add(point);

                zgc.GraphPane.AxisChange();
                zgc.Refresh();
            }

            static void test_points(ZedGraphControl zgc) {
                double[] X_dbm = { -30, -29.2, -28.4, -27.3 };
                double[] Y_ber = { 4.5E-7, 2.0E-8, 6.0E-10, 1.0E-12 };
                for (int i = 0, len = X_dbm.Length; i < len; i++) {
                    genPoint(zgc, X_dbm[i], Y_ber[i], Color.Red);
                }
            }

            static void fit_line(List<double> points, ref double a, ref double b, ref double rr) {
                List<double> X = new List<double>();
                List<double> Y = new List<double>();
                for (int i = 0, len = points.Count; i < len; i += 2) {
                    X.Add(points[i]); Y.Add(points[i + 1]);
                }

                LineFit(X.ToArray(), Y.ToArray(), ref a, ref b, ref rr);
            }

            public static LineObj fit_line(ZedGraphControl GraphViewer, LineObj line_old, List<double> points) {

                double A = 0, B = 0, RR = 0;
                fit_line(points, ref A, ref B, ref RR);

                double x1, y1, x2, y2;
                x1 = GraphViewer.GraphPane.XAxis.Scale.Min;
                y1 = A * x1 + B;
                if (y1 > GraphViewer.GraphPane.YAxis.Scale.Max) {
                    y1 = GraphViewer.GraphPane.YAxis.Scale.Max;
                    x1 = (y1 - B) / A;
                }
                y2 = GraphViewer.GraphPane.YAxis.Scale.Min;
                x2 = (y2 - B) / A;
                if (x2 > GraphViewer.GraphPane.XAxis.Scale.Max) {
                    x2 = GraphViewer.GraphPane.XAxis.Scale.Max;
                    y2 = A * x2 + B;
                }

                GraphViewer.GraphPane.GraphObjList.Remove(line_old);
                LineObj line_new = new LineObj(x1, y1, x2, y2);
                line_new.Location.CoordinateFrame = CoordType.AxisXYScale;
                GraphViewer.GraphPane.GraphObjList.Add(line_new);

                GraphViewer.GraphPane.AxisChange();
                GraphViewer.Refresh();

                return line_new;
            }

            /// <summary>
            /// y=A*x+B(y->Y,x->X)
            /// </summary>
            public static void LineFit(double[] X, double[] Y, ref double A, ref double B, ref double RR) {
                double N = X.Length;
                double sum_x = 0; double sum_y = 0; double sum_xy = 0;
                double sum_x2 = 0; double sumx_2 = 0; double sum_y2 = 0; double sumy_2 = 0;

                Calc(X, Y, ref sum_x, ref sum_y, ref sum_xy, ref sum_x2, ref sumx_2, ref sum_y2, ref sumy_2);

                A = (N * sum_xy - sum_x * sum_y) / (N * sum_x2 - sumx_2);
                B = (sum_y * sum_x2 - sum_x * sum_xy) / (N * sum_x2 - sumx_2);

                double R = (N * sum_xy - sum_x * sum_y) / Math.Sqrt((N * sum_x2 - sumx_2) * (N * sum_y2 - sumy_2));
                RR = Math.Pow(R, 2);
            }

            static void Calc(double[] X, double[] Y,
                ref double sum_x, ref double sum_y, ref double sum_xy,
                ref double sum_x2, ref double sumx_2, ref double sum_y2, ref double sumy_2) {
                sum_x = 0; sum_y = 0; sum_xy = 0;
                sum_x2 = 0; sumx_2 = 0; sum_y2 = 0; sumy_2 = 0;

                for (int i = 0, len = X.Length; i < len; i++) {
                    sum_x += X[i];
                    sum_y += Y[i];
                    sum_xy += X[i] * Y[i];
                    sum_x2 += X[i] * X[i];
                    sum_y2 += Y[i] * Y[i];
                }

                sumx_2 = sum_x * sum_x;
                sumy_2 = sum_y * sum_y;
            }
        }
    }
}
